/*
Copyright 2020 The Magma Authors.

This source code is licensed under the BSD-style license found in the
LICENSE file in the root directory of this source tree.

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package radius

import (
	"context"
	"errors"
	"net"
)

// ErrServerShutdown is returned from server Serve methods when Shutdown
// has been called and handlers are still completing.
var ErrServerShutdown = errors.New("radius: server is shutting down")

// Handler provides a handler to RADIUS server requests. When a RADIUS request
// is received, ServeRADIUS is called.
type Handler interface {
	ServeRADIUS(w ResponseWriter, r *Request)
}

// HandlerFunc allows a function to implement Handler.
type HandlerFunc func(w ResponseWriter, r *Request)

// ServeRADIUS calls h(w, p).
func (h HandlerFunc) ServeRADIUS(w ResponseWriter, r *Request) {
	h(w, r)
}

// Request is an incoming RADIUS request that is being handled by the server.
type Request struct {
	// LocalAddr is the local address on which the incoming RADIUS request
	// was received.
	LocalAddr net.Addr
	// RemoteAddr is the address from which the incoming RADIUS request
	// was sent.
	RemoteAddr net.Addr

	// Packet is the RADIUS packet sent in the request.
	*Packet

	ctx context.Context
}

// Context returns the context of the request. If a context has not been set
// using WithContext, the Background context is returned.
func (r *Request) Context() context.Context {
	if r.ctx != nil {
		return r.ctx
	}
	return context.Background()
}

// WithContext returns a shallow copy of the request with the new request's
// context set to the given context.
func (r *Request) WithContext(ctx context.Context) *Request {
	if ctx == nil {
		panic("nil ctx")
	}
	req := new(Request)
	*req = *r
	req.ctx = ctx
	return req
}

// ResponseWriter is used by RADIUS servers when replying to a RADIUS request.
type ResponseWriter interface {
	Write(packet *Packet) error
}

// SecretSource supplies RADIUS servers with the secret that should be used for
// authorizing and decrypting packets.
//
// ctx is canceled if the server's Shutdown method is called.
type SecretSource interface {
	RADIUSSecret(ctx context.Context, remoteAddr net.Addr) ([]byte, error)
}

// StaticSecretSource returns a SecretSource that uses secret for all requests.
func StaticSecretSource(secret []byte) SecretSource {
	return &staticSecretSource{secret}
}

type staticSecretSource struct {
	secret []byte
}

func (s *staticSecretSource) RADIUSSecret(ctx context.Context, remoteAddr net.Addr) ([]byte, error) {
	return s.secret, nil
}
